﻿using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using Polly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public class S3BucketFileMover
    {
        private readonly IAmazonS3 _s3Client;
        //private const long MultipartThreshold = 4L * 1024 * 1024 * 1024; // 4GB
        private const long MultipartThreshold = 50 * 1024 * 1024; 
        private const int MaxRetries = 3;
        private readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(2);
        private const int PartSizeMB = 5; // 分段大小

        public event Action<double> ProgressChanged;
        public event Action OnFinish;

        public S3BucketFileMover(IAmazonS3 s3Client)
        {
            _s3Client = s3Client;
        }

        public async Task MoveFileAsync(string bucketName, string sourceKey, string destKey)
        {
            try
            {
                // 获取文件元数据
                var metadata = await GetObjectMetadata(bucketName, sourceKey);

                // 自动选择传输方式
                if (metadata.ContentLength > MultipartThreshold)
                {
                     await MoveLargeFile(bucketName, sourceKey, destKey, metadata.ContentLength);
                }
                else
                {
                    var copyResult = await MoveSmallFile(bucketName, sourceKey, destKey);
                }


                // 删除源文件
                var delResult = await DeleteSourceFile(bucketName, sourceKey);


            }
            catch (Exception ex)
            {
                Console.WriteLine($"文件移动失败: {ex.Message}");
                throw;
            }
        }

        private async Task MoveLargeFile(
            string bucketName,
            string sourceKey,
            string destKey,
            long fileSize)
        {
            string uploadId = null;
            try
            {
                // 初始化分段上传
                uploadId = await InitiateMultipartUpload(bucketName, destKey);

                // 计算分段参数
                var partSize = PartSizeMB * 1024 * 1024;
                var partCount = (int)Math.Ceiling((double)fileSize / partSize);

                // 并行复制分段
                var copyTasks = new List<Task<CopyPartResponse>>();
                for (var partNumber = 1; partNumber <= partCount; partNumber++)
                {
                    var firstByte = (partNumber - 1) * partSize;
                    var lastByte = Math.Min(firstByte + partSize - 1, fileSize - 1);

                    copyTasks.Add(CopyPartWithRetryAsync(
                        bucketName,
                        sourceKey,
                        destKey,
                        uploadId,
                        partNumber,
                        firstByte,
                        lastByte));
                }

                var responses = await Task.WhenAll(copyTasks);

                // 完成上传
                await CompleteMultipartUpload(bucketName, destKey, uploadId, responses);

                // 更新进度
                ProgressChanged?.Invoke(100);
            }
            catch
            {
                if (uploadId != null)
                {
                    await AbortMultipartUpload(bucketName, destKey, uploadId);
                }
                throw;
            }
        }

        private async Task<string> InitiateMultipartUpload(string bucketName, string key)
        {
            var request = new InitiateMultipartUploadRequest
            {
                BucketName = bucketName,
                Key = key,
                StorageClass = S3StorageClass.Standard,
                ServerSideEncryptionMethod = ServerSideEncryptionMethod.AES256
            };

            var response = await _s3Client.InitiateMultipartUploadAsync(request);
            return response.UploadId;
        }

        private async Task<CopyPartResponse> CopyPartWithRetryAsync(
            string bucketName,
            string sourceKey,
            string destKey,
            string uploadId,
            int partNumber,
            long firstByte,
            long lastByte)
        {
            var policy = Policy
                .Handle<AmazonS3Exception>(ex =>
                    ex.ErrorCode == "RequestTimeout" ||
                    ex.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
                .WaitAndRetryAsync(MaxRetries, _ => RetryDelay);

            return await policy.ExecuteAsync(async () =>
            {
                var response = await _s3Client.CopyPartAsync(new CopyPartRequest
                {
                    SourceBucket = bucketName,
                    SourceKey = sourceKey,
                    DestinationBucket = bucketName,
                    DestinationKey = destKey,
                    UploadId = uploadId,
                    PartNumber = partNumber,
                    FirstByte = firstByte,
                    LastByte = lastByte
                });

                UpdateProgress(partNumber, (int)Math.Ceiling((double)(lastByte + 1) / PartSizeMB));
                return response;
            });
        }

        private async Task CompleteMultipartUpload(
            string bucketName,
            string key,
            string uploadId,
            IEnumerable<CopyPartResponse> responses)
        {
            var request = new CompleteMultipartUploadRequest
            {
                BucketName = bucketName,
                Key = key,
                UploadId = uploadId,
                PartETags = responses.Select(r => new PartETag(r.PartNumber, r.ETag)).ToList()
            };

            await _s3Client.CompleteMultipartUploadAsync(request);
        }

        private async Task<CopyObjectResponse> MoveSmallFile(string bucketName, string sourceKey, string destKey)
        {
            return await _s3Client.CopyObjectAsync(new CopyObjectRequest
            {
                SourceBucket = bucketName,
                SourceKey = sourceKey,
                DestinationBucket = bucketName,
                DestinationKey = destKey,
                MetadataDirective = S3MetadataDirective.COPY
            });
        }

        private async Task<DeleteObjectResponse> DeleteSourceFile(string bucketName, string key)
        {
            return await _s3Client.DeleteObjectAsync(new DeleteObjectRequest
            {
                BucketName = bucketName,
                Key = key
            });
        }

        private async Task<GetObjectMetadataResponse> GetObjectMetadata(
            string bucketName, string key)
        {
            return await _s3Client.GetObjectMetadataAsync(new GetObjectMetadataRequest
            {
                BucketName = bucketName,
                Key = key
            });
        }

        private async Task AbortMultipartUpload(
            string bucketName,
            string key,
            string uploadId)
        {
            try
            {
                await _s3Client.AbortMultipartUploadAsync(new AbortMultipartUploadRequest
                {
                    BucketName = bucketName,
                    Key = key,
                    UploadId = uploadId
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"分段上传中止失败: {ex.Message}");
            }
        }

        private void UpdateProgress(int completedPart, int totalParts)
        {
            var progress = (double)completedPart / totalParts * 100;
            ProgressChanged?.Invoke(Math.Round(progress, 2));
        }




    }
}
